# 获取脚本所在目录
$currentScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
$configFilePath = Join-Path -Path $currentScriptPath -ChildPath "deploy.json"
$winswExePath = Join-Path -Path $currentScriptPath -ChildPath "winsw.exe"
$downloadDir = Join-Path -Path $currentScriptPath -ChildPath "download"
$offlinePackageDir = Join-Path -Path $currentScriptPath -ChildPath "offline-package"

# 默认配置
$defaultConfig = @{
    DownloadUrl = ""
    StartDllName = ""
    AppDirName = "app"
    Port = "8080"
    Environment = "Production"
    ProxyAddress = ""
    Updates = @{}
}

# 显示当前配置值并获取用户输入
function GetUserInput {
    param (
        [string]$prompt,
        [string]$currentValue
    )
    if ([string]::IsNullOrWhiteSpace($currentValue)) {
        return Read-Host "$prompt"
    } else {
        $input = Read-Host "$prompt [$currentValue]"
        if ($input -eq "/") {
            return ""
        } elseif ([string]::IsNullOrWhiteSpace($input)) {
            return $currentValue
        } else {
            return $input
        }
    }
}

# 下载文件
function Download-File {
    param (
        [string]$url,
        [string]$destination
    )

    # 创建 download 目录（如果不存在）
    if (-Not (Test-Path $downloadDir)) {
        New-Item -ItemType Directory -Path $downloadDir | Out-Null
    }

    if (![string]::IsNullOrWhiteSpace($config.ProxyAddress)) {
        $parsedUrl = [uri]$url
        $newUrl = "{0}/dl/{1}" -f $config.ProxyAddress, $parsedUrl.Host + $parsedUrl.AbsolutePath
        Invoke-WebRequest -Uri $newUrl -OutFile $destination
    } else {
        Invoke-WebRequest -Uri $url -OutFile $destination
    }
}

# 确保 winsw.exe 存在
function Ensure-WinswExists {
    if (-Not (Test-Path $winswExePath)) {
        Write-Output "winsw.exe 未找到，正在下载..."

        $winswDownloadUrl = "https://soft-tools-runtime.oss-cn-chengdu.aliyuncs.com/deploy/winservice/winsw.exe"
        $destinationPath = $winswExePath

        try {
            Download-File -url $winswDownloadUrl -destination $destinationPath
            if (-Not (Test-Path $destinationPath)) {
                throw "下载winsw.exe失败"
            }
            Write-Output "winsw.exe 已下载到 $destinationPath"
        } catch {
            Write-Error "无法下载winsw.exe: $_"
            exit 1
        }
    }
}

# 解析 StartDllName 从 DownloadUrl
function Get-DllNameFromZip {
    param (
        [string]$zipFileName
    )
    # 去掉 .zip 后缀
    $baseName = [System.IO.Path]::GetFileNameWithoutExtension($zipFileName)
    # 分割字符串并取前面的部分
    $parts = $baseName -split '\.'
    if ($parts.Length -gt 1) {
        # 取除最后一部分外的所有部分
        $dllName = ($parts[0..($parts.Length - 2)] -join '.') + ".dll"
    } else {
        # 如果只有一个部分，则直接加上 .dll
        $dllName = $baseName + ".dll"
    }
    return $dllName
}

# 保存配置到 deploy.json
function Save-Config {
    # 确保 Updates 是一个 PSCustomObject
    if (-Not ($config.Updates -is [PSCustomObject])) {
        $config.Updates = [PSCustomObject]@{}
    }

    if ($PSVersionTable.PSVersion.Major -ge 6) {
        ConvertTo-Json -InputObject $config -Depth 10 | Out-File -FilePath $configFilePath -Encoding utf8BOM
    } else {
        $jsonContent = ConvertTo-Json -InputObject $config -Depth 10
        $bom = [byte[]]@(0xEF, 0xBB, 0xBF)
        $bytes = [System.Text.Encoding]::UTF8.GetBytes($jsonContent)
        [System.IO.File]::WriteAllBytes($configFilePath, $bom + $bytes)
    }
}

# 配置服务
function Config-Service {
    # 获取下载URL
    $downloadUrl = GetUserInput "请输入部署包下载地址" $config.DownloadUrl

    # 解析 StartDllName 从 DownloadUrl
    $fileName = [System.IO.Path]::GetFileName($downloadUrl)
    $startDllName = Get-DllNameFromZip -zipFileName $fileName

    # 获取应用启动dll名称
    if ([string]::IsNullOrWhiteSpace($config.StartDllName)) {
        $startDllName = GetUserInput "请输入应用启动dll名称" $startDllName
    } else {
        $startDllName = GetUserInput "请输入应用启动dll名称" $config.StartDllName
    }

    # 获取应用目录名称
    $appDirName = GetUserInput "请输入应用目录名称" $config.AppDirName

    # 获取端口
    $port = GetUserInput "请输入端口" $config.Port

    # 获取环境变量
    $environment = GetUserInput "请输入环境变量" $config.Environment

    # 获取代理地址
    $proxyAddress = GetUserInput "请输入代理地址 (可选, 输入 / 表示不使用代理)" $config.ProxyAddress

    $config = @{
        DownloadUrl = $downloadUrl
        StartDllName = $startDllName
        AppDirName = $appDirName
        Port = $port
        Environment = $environment
        ProxyAddress = $proxyAddress
        Updates = $config.Updates
    }

    Save-Config

    Write-Output "配置文件已保存: $configFilePath"
}

# 配置自定义更新
function Config-Custom-Update {
    $scriptName = GetUserInput "请输入自定义更新的名称 (例如 admin)"

    if ($config.Updates.PSObject.Properties.Name -contains $scriptName) {
        $currentDownloadUrl = $config.Updates.$scriptName.DownloadUrl
        $currentTargetDir = $config.Updates.$scriptName.TargetDir
    } else {
        $currentDownloadUrl = ""
        $currentTargetDir = ""
    }

    $downloadUrl = GetUserInput "请输入下载更新包的地址" $currentDownloadUrl
    $targetDir = GetUserInput "请输入目标目录" $currentTargetDir

    $config.Updates | Add-Member -MemberType NoteProperty -Name $scriptName -Value @{
        DownloadUrl = $downloadUrl
        TargetDir = $targetDir
    } -Force

    Save-Config

    Write-Output "自定义更新配置已保存: $configFilePath"
}

# 下载所需文件
function Download-Required-Files {
    # 创建下载目录
    if (-Not (Test-Path $downloadDir)) {
        New-Item -ItemType Directory -Path $downloadDir | Out-Null
    }

    $hasError = $false

    # 下载主程序包
    if (-not [string]::IsNullOrEmpty($config.DownloadUrl)) {
        $zipFileName = [System.IO.Path]::GetFileName($config.DownloadUrl)
        $zipFilePath = Join-Path -Path $downloadDir -ChildPath $zipFileName
        
        if (-not (Test-Path $zipFilePath)) {
            Write-Output "正在下载主程序包..."
            try {
                Download-File -url $config.DownloadUrl -destination $zipFilePath
                if (Test-Path $zipFilePath) {
                    Write-Output "主程序包已下载: $zipFileName"
                } else {
                    throw "下载完成但文件未生成"
                }
            } catch {
                Write-Error "下载主程序包失败: $_"
                $hasError = $true
            }
        } else {
            Write-Output "主程序包已存在: $zipFileName"
        }
    } else {
        Write-Error "未配置下载地址"
        $hasError = $true
    }

    # 下载自定义更新包
    foreach ($update in $config.Updates.PSObject.Properties) {
        $updateName = $update.Name
        $updateConfig = $update.Value
        
        if (-not [string]::IsNullOrEmpty($updateConfig.DownloadUrl)) {
            $zipFileName = [System.IO.Path]::GetFileName($updateConfig.DownloadUrl)
            $zipFilePath = Join-Path -Path $downloadDir -ChildPath $zipFileName
            
            if (-not (Test-Path $zipFilePath)) {
                Write-Output "正在下载自定义更新包: $updateName..."
                try {
                    Download-File -url $updateConfig.DownloadUrl -destination $zipFilePath
                    if (Test-Path $zipFilePath) {
                        Write-Output "自定义更新包已下载: $zipFileName"
                    } else {
                        throw "下载完成但文件未生成"
                    }
                } catch {
                    Write-Error "下载自定义更新包失败: $_"
                    $hasError = $true
                }
            } else {
                Write-Output "自定义更新包已存在: $zipFileName"
            }
        }
    }

    # 下载 winsw.exe
    if (-not (Test-Path $winswExePath)) {
        try {
            Ensure-WinswExists
        } catch {
            Write-Error "下载 winsw.exe 失败: $_"
            $hasError = $true
        }
    } else {
        Write-Output "winsw.exe 已存在"
    }

    if ($hasError) {
        Write-Error "下载过程中存在错误，请检查并重试"
        exit 1
    }
}

# 检查并显示当前配置
function Show-Current-Config {
    Write-Output "`n当前配置:"
    Write-Output "----------------------------------------"
    Write-Output "部署包下载地址: $($script:config.DownloadUrl)"
    Write-Output "应用启动dll名称: $($script:config.StartDllName)"
    Write-Output "应用目录名称: $($script:config.AppDirName)"
    Write-Output "端口: $($script:config.Port)"
    Write-Output "环境变量: $($script:config.Environment)"
    Write-Output "代理地址: $($script:config.ProxyAddress)"
    if ($script:config.Updates.PSObject.Properties.Count -gt 0) {
        Write-Output "`n自定义更新配置:"
        foreach ($update in $script:config.Updates.PSObject.Properties) {
            Write-Output "----------------------------------------"
            Write-Output "更新名称: $($update.Name)"
            Write-Output "下载地址: $($update.Value.DownloadUrl)"
            Write-Output "目标目录: $($update.Value.TargetDir)"
        }
    }
    Write-Output "----------------------------------------"
    
    $choice = Read-Host "`n是否需要修改配置？(Y/N)"
    return $choice -eq 'Y' -or $choice -eq 'y'
}

# 生成离线部署包
function Generate-Package {
    # 检查配置文件是否存在
    if (-Not (Test-Path $configFilePath)) {
        Write-Error "配置文件不存在，请先运行配置命令"
        return
    }

    # 检查主程序包是否存在
    if ([string]::IsNullOrEmpty($config.DownloadUrl)) {
        Write-Error "下载地址为空，请先配置下载地址"
        return
    }
    
    $mainPackageFileName = [System.IO.Path]::GetFileName($config.DownloadUrl)
    $mainPackagePath = Join-Path -Path $downloadDir -ChildPath $mainPackageFileName
    if (-Not (Test-Path $mainPackagePath)) {
        Write-Error "主程序包不存在: $mainPackageFileName`n请先下载所需文件"
        return
    }

    # 检查 winsw.exe
    if (-Not (Test-Path $winswExePath)) {
        Write-Error "winsw.exe 不存在，请确保已下载"
        return
    }

    # 复制 winsw.exe 到当前目录（如果源文件和目标文件不是同一个文件）
    $targetWinswPath = Join-Path -Path $currentScriptPath -ChildPath "winsw.exe"
    if ($winswExePath -ne $targetWinswPath) {
        Copy-Item -Path $winswExePath -Destination $currentScriptPath -Force
        Write-Output "已复制 winsw.exe"
    }

    # 生成 winsw.xml 文件
    $serviceName = [System.IO.Path]::GetFileNameWithoutExtension($config.StartDllName)
    $xmlContent = @"
<?xml version="1.0" encoding="UTF-8"?>
<service>
  <id>$serviceName</id>
  <name>$serviceName Service</name>
  <description>This service runs the $serviceName application.</description>
  <executable>dotnet</executable>
  <arguments>$($config.StartDllName) --urls=http://*:$($config.Port)/ --environment $($config.Environment)</arguments>
  <logmode>rotate</logmode>
  <logpath>%BASE%\logs</logpath>
  <logsize>1M</logsize>
  <logfiles>10</logfiles>
  <workingdirectory>%BASE%\$($config.AppDirName)</workingdirectory>
</service>
"@
    Set-Content -Path (Join-Path -Path $currentScriptPath -ChildPath "winsw.xml") -Value $xmlContent -Encoding UTF8
    Write-Output "已生成 winsw.xml 配置文件"

    # 保存最新的配置文件
    Save-Config
    Write-Output "已更新配置文件"

    Write-Output "`n部署包已生成完成！"
    Write-Output "所有必要文件已准备就绪，您可以直接将当前目录下的文件复制到目标服务器使用"
    Write-Output "包含以下文件："
    Write-Output "1. winsw.exe (Windows 服务包装器)"
    Write-Output "2. winsw.xml (服务配置文件)"
    Write-Output "3. deploy.ps1 (部署脚本)"
    Write-Output "4. deploy.json (部署配置文件)"
    Write-Output "5. download 目录，包含："
    Write-Output "   - $mainPackageFileName (主程序包)"
    if ($config.Updates.PSObject.Properties.Count -gt 0) {
        foreach ($update in $config.Updates.PSObject.Properties) {
            $updateFileName = [System.IO.Path]::GetFileName($update.Value.DownloadUrl)
            if (-not [string]::IsNullOrEmpty($updateFileName)) {
                Write-Output "   - $updateFileName (自定义更新包)"
            }
        }
    }
}

# 主循环
while ($true) {
    Write-Output "`n请选择操作:"
    Write-Output "1. 配置服务 (config)"
    Write-Output "2. 配置自定义更新 (config-u)"
    Write-Output "3. 生成部署包 (generate)"
    Write-Output "0. 退出"

    $choice = Read-Host "请输入选项编号"

    switch ($choice) {
        "1" {
            # 加载现有配置
            if (Test-Path $configFilePath) {
                try {
                    $script:config = Get-Content -Path $configFilePath -Raw | ConvertFrom-Json
                    if (-Not ($script:config.Updates -is [PSCustomObject])) {
                        $script:config.Updates = [PSCustomObject]@{}
                    }
                    Write-Output "`n当前配置:"
                    Write-Output "----------------------------------------"
                    Write-Output "部署包下载地址: $($script:config.DownloadUrl)"
                    Write-Output "应用启动dll名称: $($script:config.StartDllName)"
                    Write-Output "应用目录名称: $($script:config.AppDirName)"
                    Write-Output "端口: $($script:config.Port)"
                    Write-Output "环境变量: $($script:config.Environment)"
                    Write-Output "代理地址: $($script:config.ProxyAddress)"
                    Write-Output "----------------------------------------"
                } catch {
                    Write-Output "配置文件格式错误，将使用默认配置"
                    $script:config = $defaultConfig
                }
            } else {
                $script:config = $defaultConfig
            }
            Config-Service
        }
        "2" {
            # 检查是否已经配置了基础服务
            if (-Not (Test-Path $configFilePath)) {
                Write-Error "请先配置基础服务"
                continue
            }
            try {
                $script:config = Get-Content -Path $configFilePath -Raw | ConvertFrom-Json
                if (-Not ($script:config.Updates -is [PSCustomObject])) {
                    $script:config.Updates = [PSCustomObject]@{}
                }
                # 显示现有的自定义更新配置
                if ($script:config.Updates.PSObject.Properties.Count -gt 0) {
                    Write-Output "`n当前自定义更新配置:"
                    foreach ($update in $script:config.Updates.PSObject.Properties) {
                        Write-Output "----------------------------------------"
                        Write-Output "更新名称: $($update.Name)"
                        Write-Output "下载地址: $($update.Value.DownloadUrl)"
                        Write-Output "目标目录: $($update.Value.TargetDir)"
                    }
                    Write-Output "----------------------------------------"
                }
                Config-Custom-Update
            } catch {
                Write-Error "配置文件格式错误，请重新配置基础服务"
                continue
            }
        }
        "3" {
            # 检查是否已经配置
            if (-Not (Test-Path $configFilePath)) {
                Write-Error "请先配置服务"
                continue
            }
            try {
                $script:config = Get-Content -Path $configFilePath -Raw | ConvertFrom-Json
                if ([string]::IsNullOrEmpty($script:config.DownloadUrl)) {
                    Write-Error "未配置下载地址，请先配置服务"
                    continue
                }
                # 下载所需文件
                Write-Output "`n开始下载所需文件..."
                Download-Required-Files
                # 生成必要文件
                Write-Output "`n开始生成必要文件..."
                Generate-Package
            } catch {
                Write-Error "配置文件格式错误，请重新配置服务"
                continue
            }
        }
        "0" { exit }
        default { Write-Output "无效的选项，请重试" }
    }
} 